package edu.gmu.projects.fireresponder.beacon;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import edu.gmu.projects.fireresponder.FirstResponder;
import edu.gmu.projects.fireresponder.comm.UDPBroadcast;
import edu.gmu.projects.fireresponder.fusionengine.FusionEngine;
import edu.gmu.projects.fireresponder.fusionengine.FusionService;
import edu.gmu.projects.fireresponder.fusionengine.FusionEngine.ProbabilityCategory;
import edu.gmu.projects.fireresponder.missionplanner.SituationEnvironment;
import edu.gmu.projects.fireresponder.notificationhandler.INotificationHandlerListener;
import edu.gmu.projects.fireresponder.xml.alerts.AlertGenerator;

public class BeaconService extends Service implements LocationListener {
	private List<LocationListener> listeners = new ArrayList<LocationListener>();
	private List<INotificationHandlerListener> nListeners = new ArrayList<INotificationHandlerListener>();
	private Location location;
	private Timer beaconTimer = new Timer();
	private TimerTask beaconTask = new TimerTask() {
		@Override
		public void run() {
			if (location != null) {
				FirstResponder self = SituationEnvironment
						.getSituationEnvironment(BeaconService.this).getSelf();

				if (self != null) {
					final ProbabilityCategory HP = ProbabilityCategory.HIGHLY_PROBABLE;
					String message = "";

					if (FusionEngine.getProbabilityCategory(
							fusionService.getFireProbability(self)).compareTo(HP) >= 0) {
						message = "You are in extreme danger!\n"
								+ "Fire is very likely at your locaiton.";

						for (INotificationHandlerListener l : nListeners) {
							l.showDialogAlert(message);
							l.vibratePhoneNotification();
							l.ringRingerNotification();
						}
					} else if (FusionEngine.getProbabilityCategory(
							fusionService.getFireProbability(self, 300000))
							.compareTo(HP) >= 0) {
						// 5 minutes warning
						message = "Leave immediately!\n"
								+ "Fire is very likely 5 minutes away.";

						for (INotificationHandlerListener l : nListeners) {
							l.showDialogAlert(message);
							l.vibratePhoneNotification();
						}
					} else if (FusionEngine.getProbabilityCategory(
							fusionService.getFireProbability(self, 600000))
							.compareTo(HP) >= 0) {
						// 10 minute warning
						message = "Fire is verly likely 10 minutes away.\n"
								+ "Please proceed carefully";

						for (INotificationHandlerListener l : nListeners) {
							l.showDialogAlert(message);
						}
					} else if (FusionEngine.getProbabilityCategory(
							fusionService.getFireProbability(self, 900000))
							.compareTo(HP) >= 0) {
						// 15 minute warning
						message = "Fire is very likely 15 minutes away.";

						for (INotificationHandlerListener l : nListeners) {
							l.showToastMessage(message);
						}
					} else if (FusionEngine.getProbabilityCategory(
							fusionService.getFireProbability(self, 1800000))
							.compareTo(HP) >= 0) {
						// 30 minute warning
						message = "Fire is very likely a half hour away";

						for (INotificationHandlerListener l : nListeners) {
							l.showToastMessage(message);
						}
					} else if (FusionEngine.getProbabilityCategory(
							fusionService.getFireProbability(self, 3600000))
							.compareTo(HP) >= 0) {
						// 60 minute warning
						message = "Fire is very likely a half hour away";

						for (INotificationHandlerListener l : nListeners) {
							l.showToastMessage(message);
						}
					}

					String msg = new AlertGenerator()
							.generateFirstResponderLocationAlert(self.getId(),
									Calendar.getInstance(), location
											.getLatitude(), location
											.getLongitude());
					UDPBroadcast.getUDPBroadcast().send(msg);

					Log.i("BeaconService", String.format(
							"%s sent beacon message", self.getId()));
				}
			}

			Log.i("BeaconService", "Ran beacon task");
		}
	};

	public class LocalBinder extends Binder {
		public BeaconService getService() {
			return BeaconService.this;
		}
	}

	private final IBinder mBinder = new LocalBinder();

	@Override
	public IBinder onBind(Intent arg) {
		return mBinder;
	}

	@Override
	public void onCreate() {
		super.onCreate();
		getLocation();

		bindService(new Intent(this, FusionService.class), fusionConnection,
				BIND_AUTO_CREATE);

		Log.i("BeaconService", "Created BeaconService service");
	}

	@Override
	public void onDestroy() {
		unbindService(fusionConnection);
		beaconTimer.cancel();

		super.onDestroy();
	}

	private FusionService fusionService;

	private ServiceConnection fusionConnection = new ServiceConnection() {
		public void onServiceConnected(ComponentName name, IBinder service) {
			fusionService = ((FusionService.LocalBinder) service).getService();
			beaconTimer.scheduleAtFixedRate(beaconTask, 0, 30000);

			Log.i("BeaconService", "Connected to FusionService");
		}

		public void onServiceDisconnected(ComponentName name) {
			fusionService = null;

			Log.i("BeaconService", "Disconnected from RelevanceEngineService");
		}
	};

	public boolean addResponderStatusListener(INotificationHandlerListener l) {
		return nListeners.add(l);
	}

	public boolean removeResponderStatusListener(INotificationHandlerListener l) {
		return nListeners.remove(l);
	}

	public boolean addListener(LocationListener l) {
		return listeners.add(l);
	}

	public boolean removeListener(LocationListener l) {
		return listeners.remove(l);
	}

	private void getLocation() {
		LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
		Criteria criteria = new Criteria();
		criteria.setAccuracy(Criteria.ACCURACY_COARSE);
		String provider = locationManager.getBestProvider(criteria, true);

		locationManager.requestLocationUpdates(provider, 1000, 0, this);
		location = locationManager.getLastKnownLocation(provider);

		if (location != null) {
			setCurrentFirstResponderLoction(location.getLatitude(), location
					.getLongitude());
		}
	}

	private void setCurrentFirstResponderLoction(double lat, double lng) {
		edu.gmu.projects.fireresponder.environment.Location myLoc = new edu.gmu.projects.fireresponder.environment.Location(
				lat, lng);

		FirstResponder self = SituationEnvironment.getSituationEnvironment(this)
				.getSelf();

		if (self != null) {
			self.setLocation(myLoc);
		}
	}

	public Location getCurrentLocation() {
		return new Location(location);
	}

	public void onLocationChanged(Location location) {
		Log.i("BeaconService", "onLocationChanged");

		this.location = location;

		if (location != null) {
			setCurrentFirstResponderLoction(location.getLatitude(), location
					.getLongitude());
		}

		for (LocationListener l : listeners) {
			l.onLocationChanged(location);
		}
	}

	public void onProviderDisabled(String provider) {
		Log.i("BeaconService", "onProviderDisabled");

		LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
		locationManager.removeUpdates(this);

		for (LocationListener l : listeners) {
			l.onProviderDisabled(provider);
		}
	}

	public void onProviderEnabled(String provider) {
		Log.i("BeaconService", "onProviderEnabled");

		getLocation();

		for (LocationListener l : listeners) {
			l.onProviderEnabled(provider);
		}
	}

	public void onStatusChanged(String provider, int status, Bundle extras) {
		Log.i("BeaconService", "onStatusChanged");

		if (status != LocationProvider.AVAILABLE) {
			LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
			locationManager.removeUpdates(this);
		}

		for (LocationListener l : listeners) {
			l.onStatusChanged(provider, status, extras);
		}
	}
}